package com.dries.netty.chat.session;

import cn.hutool.json.JSONUtil;
import com.dries.netty.chat.entity.User;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.ChannelGroupFuture;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.ImmediateEventExecutor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Dries
 * @email 1451691457@qq.com
 */
@Slf4j
@Data
public class SessionMap {

    /**
     * 单例
     */
    private static SessionMap singleInstance = new SessionMap();

    public static SessionMap getSingleInstance() {
        return singleInstance;
    }

    //会话集合
    private ConcurrentHashMap<String, ServerSession> sessionMap = new ConcurrentHashMap<>();

    //组的映射
    private ConcurrentHashMap<String, ChannelGroup> groupMap = new ConcurrentHashMap<>();

    /**
     * 增加session 的通道到组
     */
    private void addChannelGroup(ServerSession session) {
        String groupName = session.getGroup();
        if (StringUtils.isEmpty(groupName)) {
            //组为空，直接返回
            return;
        }
        ChannelGroup group = groupMap.get(groupName);
        if (null == group) {
            group = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE);
            groupMap.put(groupName, group);
        }
        group.add(session.getChannel());
    }

    /**
     * 和 channel 通道实现双向绑定
     */

    private ServerSession add(ServerSession session) {

        sessionMap.put(session.getSessionId(), session);
        log.info("用户登录:id={},name={},在线总数: {} ",
                session.getUser().getUserId(), session.getUser().getNickName(), sessionMap.size());

        //增加session 的通道到组
        addChannelGroup(session);
        return session;
    }

    /**
     * 解除绑定关系
     */
    public ServerSession remove(ServerSession session) {
        Channel channel = session.getChannel();
        SessionMap.getSingleInstance().removeSession(session.getSessionId());

        //从组中移除通道
        ChannelGroup group = groupMap.get(session.getGroup());
        if (null != group) {
            group.remove(channel);
        }
        return session;
    }

    /**
     * 获取session对象
     */
    public ServerSession getSession(String sessionId) {
        return sessionMap.getOrDefault(sessionId, null);
    }

    /**
     * 删除session
     */
    public void removeSession(String sessionId) {
        if (!sessionMap.containsKey(sessionId)) {
            return;
        }
        ServerSession s = sessionMap.get(sessionId);
        sessionMap.remove(sessionId);
        log.info("用户下线:id={}, 在线总数:{}", s.getUser().getUserId(), sessionMap.size());
    }

    public void shutdownGracefully() {

        for (ChannelGroup channel : groupMap.values()) {
            channel.close();
        }

    }

    public void sendToOthers(Map<String, String> result, ServerSession session) {
        //获取组
        ChannelGroup group = groupMap.get(session.getGroup());
        if (null == group) {
            return;
        }

        String json = JSONUtil.toJsonStr(result);

        //自己发送的消息不返回给自己
        Channel channel = session.getChannel();
        //从组中移除通道
        group.remove(channel);
        ChannelGroupFuture future = group.writeAndFlush(new TextWebSocketFrame(json));
        future.addListener(f ->
        {
            log.debug("sendToOthers finished:{}", json);
            group.add(channel);

        });
    }

    public void sendToAll(String json, ServerSession session) {
        //获取组
        ChannelGroup group = groupMap.get(session.getGroup());
        if (null == group) {
            return;
        }

        ChannelGroupFuture future = group.writeAndFlush(new TextWebSocketFrame(json));
        future.addListener(f ->
                log.debug("sendToAll finished:{}", json));
    }

    public void addSession(Map<String, String> result, ServerSession session) {
        add(session);
        String json = JSONUtil.toJsonStr(result);
        sendToAll(json, session);
    }

    public Set<User> getGroupUsers(String room) {
        //获取组
        ChannelGroup group = groupMap.get(room);
        if (null == group) {
            return null;
        }
        Set<User> userSet = new HashSet<>();
        for (Channel next : group) {
            userSet.add(ServerSession.getSession(next).getUser());
        }
        return userSet;

    }

    public List<String> getGroupNames() {
        return new ArrayList<>(groupMap.keySet());
    }

    /**
     * 关闭连接， 关闭前发送一条通知消息
     */
    public void closeSession(ServerSession session, String echo) {
        ChannelFuture sendFuture = session.getChannel().writeAndFlush(new TextWebSocketFrame(echo));
        sendFuture.addListener((ChannelFutureListener) future -> {
            log.debug("last package send finished:{},channel will closed", echo);
            future.channel().close();
        });

    }

    /**
     * 关闭连接
     */
    public void closeSession(ServerSession session) {

        ChannelFuture sendFuture = session.getChannel().close();
        sendFuture.addListener((ChannelFutureListener) future -> log.debug("closeSession finished,Nickname:{}", session.getUser().getNickName()));

    }

    /**
     * 发送消息
     *
     * @param ctx 上下文
     * @param msg 待发送的消息
     */
    public void sendMsg(ChannelHandlerContext ctx, String msg) {
        ChannelFuture sendFuture = ctx.writeAndFlush(new TextWebSocketFrame(msg));
        sendFuture.addListener(f ->
                log.debug("send finished:{}", msg));
    }

}
